package org.xx.armory.log4j2;

import org.apache.logging.log4j.Logger;
import org.apache.logging.log4j.core.config.plugins.Plugin;
import org.apache.logging.log4j.core.config.plugins.PluginAttribute;
import org.apache.logging.log4j.core.config.plugins.PluginFactory;
import org.apache.logging.log4j.nosql.appender.NoSqlProvider;
import org.apache.logging.log4j.status.StatusLogger;
import org.xx.armory.commons.CalledBy;
import org.xx.armory.db.JedisPoolFactory;
import redis.clients.jedis.JedisPool;

/**
 * 使用Redis数据库记录日志的提供程序。
 * <p>该类型不抛出任何异常，因为此时日志系统可能尚未建立，无法通过日志系统记录异常。凡是参数验证出错的地方都使用 {@link StatusLogger} 记录日志。</p>
 */
@Plugin(name = "Redis",
        category = "Core",
        printObject = true)
public class RedisProvider
        implements NoSqlProvider<RedisConnection> {
    private static final Logger LOGGER = StatusLogger.getLogger();

    private final JedisPool pool;
    private final String key;

    /**
     * 构造Redis数据库提供程序的实例。
     * <p>此构造函数不应当直接调用，而是通过工厂方法 {@link #newInstance(String, int, String)} 来调用。</p>
     *
     * @param pool
     *         访问Redis数据库的连接池。
     * @param key
     *         保存日志的键名称。
     */
    private RedisProvider(
            JedisPool pool,
            String key
    ) {
        if (pool == null) {
            LOGGER.fatal("argument \"pool\" cannot be null");
            this.pool = null;
        } else {
            this.pool = pool;
        }

        if (key == null) {
            LOGGER.fatal("argument \"key\" cannot be null");
            this.key = "";
        } else {
            key = key.trim();
            if (key.isEmpty()) {
                LOGGER.fatal("argument \"key\" cannot be blank");
                this.key = "";
            } else {
                this.key = key;
            }
        }
    }

    /**
     * 创建Redis数据库提供程序。
     *
     * @param uri
     *         连接的Redis数据库的地址。
     * @param timeout
     *         连接Redis数据库的超时时间(毫秒)
     * @param key
     *         保存日志的键名称。
     * @return 已创建的Redis数据库提供程序。
     * @throws IllegalArgumentException
     *         如果参数{@code uri}是{@code null}或者只包含空白字符，或者参数{@code uri}不是正确的连接Redis数据库的URI，或者参数{@code timeout}小于{@code 0}，或者参数{@code key}是{@code null}或者只包含空白字符。
     */
    @PluginFactory
    @CalledBy("log4j2")
    public static RedisProvider newInstance(
            @PluginAttribute("uri") String uri,
            @PluginAttribute("timeout") int timeout,
            @PluginAttribute("key") String key
    ) {
        final JedisPool pool = JedisPoolFactory.createPool(uri, timeout);
        return new RedisProvider(pool, key);
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public RedisConnection getConnection() {
        return new RedisConnection(pool.getResource(), this.key);
    }

}
